/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.ufba.lasid.v1.jitclouds.mda.cloud;

import br.ufba.lasid.v1.jitclouds.mda.api.JiTFTManagerAPI;
import br.ufba.lasid.v1.jitclouds.mda.api.JiTFTDeployerAPI;
import br.ufba.lasid.v1.jitclouds.mda.api.JiTFTRepositoryAPI;
import br.ufba.lasid.v1.jitclouds.mda.api.JiTProvisionerAPI;
import br.ufba.lasid.v1.jitclouds.mda.api.JiTFTPlannerAPI;
import br.ufba.lasid.v1.jitclouds.mda.api.JiTFTAssessmentAPI;
import br.ufba.lasid.v1.jitclouds.mda.MDA;
import br.ufba.lasid.v1.jitclouds.mda.datatypes.FTServiceLevel;
import br.ufba.lasid.v1.jitclouds.mda.datatypes.plan.Plan;
import br.ufba.lasid.v1.jitclouds.mda.datatypes.ft.IReplicable;
import br.ufba.lasid.v1.jitclouds.mda.datatypes.ft.IReplicableList;
import br.ufba.lasid.v1.jitclouds.mda.datatypes.report.Report;
import br.ufba.lasid.v1.jitclouds.mda.datatypes.virtualization.IVirtualMachine;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author aliriosa
 */
public class JiTFTManager implements JiTFTManagerAPI, JiTFTAssessmentAPI, JiTFTDeployerAPI, JiTFTPlannerAPI, JiTProvisionerAPI{
   
    private JiTFTAssessmentAPI assessement = null;
    private JiTFTDeployerAPI deployer = null;
    private JiTFTPlannerAPI planner = null;
    private JiTProvisionerAPI provisioner = null;
    private JiTFTRepositoryAPI repository = null;
   
    @Override
    public void start() {
        /**
         * this method is going to be implemented when we integrate with MBF 
         * components.
         */
    }

    @Override
    public void stop() {
        /**
         * this method is going to be implemented when we integrate with MBF 
         * components.
         */        
    }

    @Override
    public Report replicate(String resourceID, FTServiceLevel ftsLevel) throws Exception {
        /* Check if the fault tolerance service level and its respective constraints can be applied. */
        Report rEvaluation = evaluate(ftsLevel);
        
        /* Stamp the evaluation result as a replication context report. */
        rEvaluation.set(MDA.Context, MDA.Contexts.CloudReplicationDeployment);
        
    /*  // what if the evaluation reports fail?
        if(!checkReport(rEvaluation)) return null;
        */
                
        /* Get the replication plan for assigned resource using the specified 
         * service level and constraints and the obtained evaluation report.
         */
        Plan rPlan = plan(resourceID, ftsLevel, rEvaluation);               
        
        /* Extract the related allocation plan from the replication plan */
        
        Plan aPlan = rPlan.get(MDA.AllocationPlan);
        
        /* Allocate the respective resource replicas */
        IReplicableList replicas = allocate(aPlan);                
                
        /* Synchronize the initial state between replicas */
        Report fullSyncReport = syncState(resourceID, replicas);
        
        fullSyncReport.set(MDA.Context, MDA.Contexts.CloudSynchronization);
        
/*      // what if the sync state procedure report some error?
 *      if(!checkReport(fullSyncReport)) return null;
 */
        
        /* Install the failover in the set of replicated resources */        
        Report dReport = installFailover(replicas);
        
        /* Prepare the failover deployment report. It must contain the evaluation, 
         * synchronization, and replication reports. Moreover, the replication
         * plan also has to be contained by the deployment report.
         */
        dReport.set(MDA.Context, MDA.Contexts.CloudReplicationDeployment);        
//        dReport.constructName(resourceID);
//        dReport.set(MDA.HostEndPoint, getConfiguration().get("endpoint"));
        dReport.set(MDA.EvaluationReport, rEvaluation);
        dReport.set(MDA.SynchronizationReport, fullSyncReport);        
        dReport.set(MDA.ReplicationPlan, rPlan);
        
        return dReport;
     
    }
    
    
    
    @Override
    public Plan plan(String resourceid, FTServiceLevel ftsLevel, Report cContextInfo) {
        return planner.plan(resourceid, ftsLevel, cContextInfo);
    }

    @Override
    public Plan plan(String resourceid, FTServiceLevel ftsLevel, Plan inPlan, MDA.Contexts context) {
        return planner.plan(resourceid, ftsLevel, inPlan, context);
    }
    
    

    @Override
    public Report evaluate(FTServiceLevel ftsLevel) throws Exception{
        return assessement.evaluate(ftsLevel);
    }

    @Override
    public IReplicableList allocate(Plan plan) {
        return provisioner.allocate(plan);
    }
    
    public Report syncState(String sourceRID, IReplicableList replicas) {
        
        Report report = new Report();
        
        for(IReplicable replica: replicas){
            
            //String repname = "syncReport." + sourceRID + "." + replica.getID() ;
            if(!replica.isUpdated()){
                Report syncReport = provisioner.syncState(sourceRID, replica.getID());
                report.put((String)syncReport.get("name"), syncReport);            
            }
                                    
        }
        
        return report;
    }
    
    @Override
    public Report syncState(String sourceRID, String... destinationRID) {
     return provisioner.syncState(sourceRID, destinationRID);
    }

    @Override
    public Report installFailover(IReplicableList replicas) {
        return deployer.installFailover(replicas);
    }    
        
    public boolean checkReport(Report report){
        return true;
    }

    @Override
    public Report recovery(String resourceID) throws Exception{
        /**
         * Retrieve the related replication plan from jit clouds repository.
         */
        Plan repPlan = repository.retrieve(resourceID, MDA.Contexts.DCReplication);
        
        /**
         * Obtain the service level related to the replicated schema.
         */       
        FTServiceLevel ftsLevel = repPlan.get(MDA.SLA);
        
        /**
         * What if the replication plan is null?.
         */
        
        /**
         * Obtain a recovery plan for the resource using the specified service 
         * level, and the related replication plan.
         */
        Plan recPlan = plan(resourceID, ftsLevel, repPlan, MDA.Contexts.DCRecovery);
        
        /**
         * Extract the allocation plan from replication plan.
         */
        
        Plan aPlan = recPlan.get(MDA.AllocationPlan);
        
        /**
         * Allocate the replicas which must be recoveried
         */
        
        IReplicableList replicas =  allocate(aPlan);
        
        /* Synchronize the initial state between replicas */
        Report fullSyncReport = syncState(resourceID, replicas);
        
        fullSyncReport.set(MDA.Context, MDA.Contexts.CloudSynchronization);
        
        
        Report dReport = installFailover(replicas);
        
        repPlan = update(repPlan, recPlan);
        
        repository.update(repPlan);
        
        
        /* Prepare the failover deployment report. It must contain the evaluation, 
         * synchronization, and replication reports. Moreover, the replication
         * plan also has to be contained by the deployment report.
         */
        dReport.set(MDA.Context, MDA.Contexts.CloudReplicationDeployment);        
        dReport.set(MDA.ReplicationPlan, repPlan);
        dReport.set(MDA.RecoveryPlan, recPlan);        
        dReport.set(MDA.SynchronizationReport, fullSyncReport);        
                
        return dReport;
    }

    @Override
    public Plan update(Plan oldReplicationPlan, Plan newRecoveryPlan) {
        return planner.update(oldReplicationPlan, newRecoveryPlan);
    }

    @Override
    public void notifyFaulty(String resourceID) throws Exception{
        /**
         * Retrieve the related replication plan from jit clouds repository.
         */
        Plan repPlan = repository.retrieve(resourceID, MDA.Contexts.DCReplication);

        /**
         * Retrieve in the service level related to this resource.
         */        
        FTServiceLevel ftLevel = repPlan.get(MDA.SLA);
               
        try {
            IVirtualMachine vm = MDA.createVM();
            if(vm != null){
                vm.setID(resourceID);
                vm = repository.retrieve(vm);            
                vm.setStatus(IVirtualMachine.VMStatus.Faulty);
            }
            
            /**
            * Get the automatic procedure option.
            */

            boolean allowAutomatic = (Boolean)ftLevel.get(FTServiceLevel.AllowAutonomic);

            /**
            * If service level consider automatic recover then do recovery.
            */
            if(allowAutomatic){
                /**
                * Perform the automatic recovery.
                */
                Report dReport = recovery(resourceID);            

                /**
                * Save the recovery report.
                */
                repository.save(dReport);
            }                                   
            
        } catch (Exception ex) {
            Logger.getLogger(JiTFTManager.class.getName()).log(Level.SEVERE, null, ex);
        }                
        
    }                
    
}
